/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "accessibility_accessibility_event_handler.h"
#include "accessible_ability_manager_service.h"
#include "accessibility_screen_reader_service.h"
#include <string>
#include <vector>

#include "utils.h"

#define SWITCH_BEGIN(eventType) switch (eventType) {
#define SWITCH_CASE(event_case, func) case event_case:\
    {\
        func(uiEvent);\
        break;\
    }

#define SWITCH_END() default:\
    {\
        HILOG_WARN("AccessibilityEventHandler::OnAccessibilityEvent, default case, need check.");\
        break;\
    }\
}

    //SWITCH_CASE(EventType::TYPE_VIEW_INVALID, HandleViewInvalidEvent)\
    // SWITCH_CASE(EventType::TYPE_VIEW_CLICKED_EVENT, HandleViewClickedEvent)\
    // SWITCH_CASE(EventType::TYPE_VIEW_LONG_CLICKED_EVENT, HandleViewLongClickedEvent)\
    // SWITCH_CASE(EventType::TYPE_VIEW_SELECTED_EVENT, HandleViewSelectedEvent)\
    // SWITCH_CASE(EventType::TYPE_VIEW_FOCUSED_EVENT, HandleViewFocusedEvent)\
    // SWITCH_CASE(EventType::TYPE_VIEW_TEXT_UPDATE_EVENT, HandleViewTextUpdateEvent)\
    // SWITCH_CASE(EventType::TYPE_NOTIFICATION_UPDATE_EVENT, HandleNotificationUpdateEvent)\
    // SWITCH_CASE(EventType::TYPE_VIEW_HOVER_EXIT_EVENT, HandleViewHoverExitEvent)\
    // SWITCH_CASE(EventType::TYPE_TOUCH_GUIDE_GESTURE_BEGIN, HandleTouchGuideGestureBeginEvent)\
    // SWITCH_CASE(EventType::TYPE_TOUCH_GUIDE_GESTURE_END, HandleTouchGuideGestureEndEvent)\
    // SWITCH_CASE(EventType::TYPE_VIEW_SCROLLED_EVENT, HandleViewScrolledEvent)\
    // SWITCH_CASE(EventType::TYPE_VIEW_TEXT_SELECTION_UPDATE_EVENT, HandleViewTextSelectionUpdateEvent)\
    // SWITCH_CASE(EventType::TYPE_PUBLIC_NOTICE_EVENT, HandlePublicNoticeEvent)\

    // focus不能处理，会跟其他的比如hoverenter、上下滑冲突导致出现闪烁
    // SWITCH_CASE(EventType::TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED_EVENT, ExecuteActionOnAccessibilityFocused)\
    // SWITCH_CASE(EventType::TYPE_VIEW_ACCESSIBILITY_FOCUSED_EVENT, ExecuteActionOnAccessibilityFocused)\
    
    // SWITCH_CASE(EventType::TYPE_VIEW_TEXT_MOVE_UNIT_EVENT, HandleViewTextMoveUnitEvent)\
    // SWITCH_CASE(EventType::TYPE_TOUCH_GUIDE_BEGIN, HandleTouchGuideBeginEvent)\
    // SWITCH_CASE(EventType::TYPE_TOUCH_GUIDE_END, HandleTouchGuideEndEvent)\
    // SWITCH_CASE(EventType::TYPE_TOUCH_BEGIN, HandleTouchBeginEvent)\
    // SWITCH_CASE(EventType::TYPE_TOUCH_END, HandleTouchEndEvent)\
    // SWITCH_CASE(EventType::TYPE_WINDOW_UPDATE, HandleWindowUpdateEvent)\
    // SWITCH_CASE(EventType::TYPE_INTERRUPT_EVENT, HandleInterruptEvent)\
    // SWITCH_CASE(EventType::TYPE_GESTURE_EVENT, HandleGestureEvent)\
    // SWITCH_CASE(EventType::TYPE_VIEW_REQUEST_FOCUS_FOR_ACCESSIBILITY, HandleViewRequestFocusForAccessibilityEvent)\
    // SWITCH_CASE(EventType::TYPE_VIEW_SCROLLED_START, HandleViewScrolledStartEvent)\
    // SWITCH_CASE(EventType::TYPE_PAGE_CLOSE, HandlePageCloseEvent)\
    // SWITCH_CASE(EventType::TYPE_VIEW_ANNOUNCE_FOR_ACCESSIBILITY, HandleViewAnnounceForAccessibilityEvent)\
    // SWITCH_CASE(EventType::TYPE_MAX_NUM, HandleMaxNumEvent)\
    // SWITCH_CASE(EventType::TYPES_ALL_MASK, HandleTypesAllMaskEvent)

#define ACCESSIBILITY_EVENT_CASES() \
    SWITCH_CASE(EventType::TYPE_VIEW_HOVER_ENTER_EVENT, HandleViewHoverEnterEvent)\
    SWITCH_CASE(EventType::TYPE_PAGE_CONTENT_UPDATE, HandlePageUpdate)\
    SWITCH_CASE(EventType::TYPE_PAGE_STATE_UPDATE, HandlePageUpdate)\
    SWITCH_CASE(EventType::TYPE_PAGE_OPEN, HandlePageUpdate)\



namespace OHOS {
namespace Accessibility {
AccessibilityEventHandler::AccessibilityEventHandler() {}
AccessibilityEventHandler::~AccessibilityEventHandler() {}
void AccessibilityEventHandler::OnAccessibilityEvent(const AccessibilityEventInfo &uiEvent)
{
    HILOG_DEBUG("elementId[%{public}" PRId64 "] componentId[%{public}" PRId64 "] windowId[%{public}d] currentIndex[%{public}d] eventType[0x%{public}x] gestureType[%{public}d]",
        uiEvent.GetAccessibilityId(),uiEvent.GetViewId(), uiEvent.GetWindowId(), uiEvent.GetCurrentIndex(), uiEvent.GetEventType(), uiEvent.GetGestureType());
    EventType eventType=uiEvent.GetEventType();

    SWITCH_BEGIN(eventType)
    ACCESSIBILITY_EVENT_CASES()
    SWITCH_END()
}

#define CONTENT_TAG "CONTENT_TAG"
void AccessibilityEventHandler::HandleViewHoverEnterEvent(const AccessibilityEventInfo &uiEvent)
{
    HILOG_DEBUG();
    int64_t start_time = Utils::GetSystemTime();
    int64_t end_time;
    int64_t componentId=uiEvent.GetViewId();
    int32_t windowId=uiEvent.GetWindowId();
    HILOG_DEBUG("componentId[%{public}" PRId64 "] windowId[%{public}d]", componentId, windowId);
    const AccessibilityElementInfo &elementInfo=uiEvent.GetElementInfo();
    auto &sr=Singleton<ScreenReader>::GetInstance();
    HILOG_DEBUG(CONTENT_TAG "textAnnouncedForAccessibility: [%{public}s], getContent: [%{public}s]", 
        uiEvent.GetTextAnnouncedForAccessibility().c_str(), sr.GetContent(elementInfo).c_str());
    // sr.HilogElementInfo(elementInfo);
    if(componentId == AccessibilityElementInfo::UNDEFINED_ACCESSIBILITY_ID){
        HILOG_DEBUG("componentId is undefined");
        return;
    }
    if(!sr.CheckFocusable(elementInfo)){
        HILOG_DEBUG("not focusable, start to find focusable parent");
        sr.DrawFocus(sr.FindFocusableParentElement(elementInfo));
        end_time = Utils::GetSystemTime();
        HILOG_DEBUG(PERFORMANCE_TEST "HandleViewHoverEnterEvent cost time: %{public}lld ms", end_time - start_time);
        return;
    }
    HILOG_DEBUG("componentId[%{public}lld", elementInfo.GetAccessibilityId());
    sr.DrawFocus(elementInfo);
    end_time = Utils::GetSystemTime();
    HILOG_DEBUG(PERFORMANCE_TEST "HandleViewHoverEnterEvent cost time: %{public}lld ms", end_time - start_time);
}

void AccessibilityEventHandler::ExecuteActionOnAccessibilityFocused(const AccessibilityEventInfo &uiEvent)
{
    HILOG_DEBUG("elementId[%{public}" PRId64 "] windowId[%{public}d] eventType[0x%{public}x]", uiEvent.GetAccessibilityId(), uiEvent.GetWindowId(), uiEvent.GetEventType());
    HILOG_DEBUG("current focused elementId[%{public}" PRId64 "]", Singleton<AccessibleAbilityManagerService>::GetInstance().GetFocusElementId());
    switch (uiEvent.GetEventType())
    {
    case EventType::TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED_EVENT:
        Singleton<AccessibleAbilityManagerService>::GetInstance().ExecuteActionOnAccessibilityFocused(ActionType::ACCESSIBILITY_ACTION_CLEAR_ACCESSIBILITY_FOCUS);
        break;

    case EventType::TYPE_VIEW_ACCESSIBILITY_FOCUSED_EVENT:
        Singleton<AccessibleAbilityManagerService>::GetInstance().ExecuteActionOnAccessibilityFocused(ActionType::ACCESSIBILITY_ACTION_ACCESSIBILITY_FOCUS);
    
    default:
        HILOG_DEBUG("default, need to check");
        break;
    }
}

void AccessibilityEventHandler::HandlePageUpdate(const AccessibilityEventInfo &uiEvent)
{
    HILOG_DEBUG("eventType[0x%{public}x]", uiEvent.GetEventType());
    int64_t start_time = Utils::GetSystemTime();
    int64_t end_time;
    auto &sr=Singleton<ScreenReader>::GetInstance();
    auto &aams=Singleton<AccessibleAbilityManagerService>::GetInstance();
    // update new focus 
    int32_t newWindowId=uiEvent.GetWindowId();
    int32_t newPageId=uiEvent.GetPageId();
    if(newWindowId == AccessibilityElementInfo::UNDEFINED_WINID_ID){
        HILOG_DEBUG("no windowId in pageUpdate uievent");
        return;
    }
    // find history componentId in cache
    int64_t newComponentId = sr.GetCacheElementId(newWindowId, newPageId);
    AccessibilityElementInfo newFocusComponent={};
    if (newComponentId == AccessibilityElementInfo::UNDEFINED_ACCESSIBILITY_ID){
        HILOG_DEBUG("find history node failed");
        // if not found, set the first node in current page as focus
        if(!sr.FindFirstElement(newFocusComponent)){
            HILOG_DEBUG("find first focusable node failed");
        }
    }else{
        if(!aams.FindElementInfoByAccessibilityId(newComponentId, newWindowId, newFocusComponent)){
            HILOG_DEBUG("find history node by cacheid failed");
        }
    }
    if(!sr.DrawFocus(newFocusComponent)){
        HILOG_DEBUG("draw focus failed");
    }
    end_time = Utils::GetSystemTime();
    HILOG_DEBUG(PERFORMANCE_TEST "handlePageUpdate cost time: %{public}lld ms", end_time - start_time);
    HILOG_DEBUG("pageUpdate end");
}

}// namespace Accessibility
}// namespace OHOS

